﻿package extremefx.remoting.encoders {

	/**
	 * @author Marcelo Volmaro
	 */
	public class WDDX {
		
		/**
		 * Compiles AS3 objects to WDDX.
		 * Type are mapped against these rules:
		 * 
		 * <ul>
		 * <li>String to string</li>
		 * <li>Number to string</li>
		 * <li>Boolean to boolean</li>
		 * <li>Date to date</li>
		 * <li>Array to array</li>
		 * <li>Object to struct</li>
		 * </ul>
		 *
		 * @param data AS3 object.
		 * @return WDDX representation of the object.
		 */
		public function serialize(pData:Object):String {
			var o:String = "<wddxPacket version='1.0'><header/><data>";
			o += _encode(pData);
			o += "</data></wddxPacket>";
			return o;
		}
		
		/**
		 * Decompiles WDDX to AS3 objects.
		 * Type are mapped against these rules:
		 * 
		 * <ul>
		 * <li>string, number to String</li>
		 * <li>boolean to Boolean</li>
		 * <li>array to Array</li>
		 * <li>struct to Object</li>
		 * <li>query to Recordset</li>
		 * </ul>
		 *
		 * @param data WDDX representation as XML.
		 * @return AS3 WDDX representation.
		 */
		 
		public function deserialize(wddx:String):Object {
			var x:XML = new XML(wddx);
			return _decode(x.data.children()[0]);
		}
		
		private function _encode(data:*):String {
			var o:String = "";
			if ( data is String )
				o = _toString(data);
				
			else if (data is Number)
				o = _toString(data);
				
			else if (data is Boolean)
				o = _toBoolean(data);
				
			else if (data is Date)
				o = _toDate(data);
				
			else if (data is Array)
				o = _toArray(data);

			else if ( data is Object )
				o = _toStruct(data);
				
			return o;
		}
		
		private function _toString(data:String):String {
			return "<string>"+data.toString()+"</string>";
		}
		
		private function _toBoolean(data:Boolean):String {
			return "<boolean value='"+data.toString()+"' />";
		}
		
		private function _toDate(data:Date):String {
			return "<dateTime>"+data.toString()+"</dateTime>";
		}
		
		private function _toArray(data:Array):String {
			var s:String = "<array length='"+data.length+"'>";
			
			for (var i:Number=0; i<data.length; i++) {
				s += _encode( data[i] );
			}
			
			s += "</array>";
			return s;
		}
		
		private function _toStruct(data:Object):String {
			var s:String = "<struct>";
			for (var i:String in data) {
				s += "<var name='"+i+"'>";
				s += _encode( data[i] );
				s += "</var>";
			}
			
			s += "</struct>";
			return s;
		}
		
		private function _decode(data:XML):Object {
			var o:Object = {};
			
			switch (data.name().toString().toLowerCase()) {
				case "string":
					o = _fromString(data);
					break;
					
				case "boolean":
					o = _fromBoolean(data);
					break;
					
				case "array":
					o = _fromArray(data);
					break;
					
				case "struct":
					o = _fromStruct(data);
					break;
					
				case "dateTime":
					o = _fromString(data);
					break;
					
				default:
					o = _fromString(data);
					break;
			}
			
			return o;
		}
		
		private function _fromString(data:XML):String {
			return data.text();
		}
		
		private function _fromBoolean(data:XML):Boolean {
			return data.@value;
		}
		
		private function _fromArray(data:XML):Array {
			var o:Array = [];
			
			for (var i:int=0; i<parseInt(data.@length.toString()); i++) {
				o.push( _decode(data.children()[i]) );
			}
			
			return o;
		}
		
		private function _fromStruct(data:XML):Object {
			var o:Object = {};
			var xList:XMLList = data["var"];
			for each (var v:XML in xList) {
				o[v.@name] = _decode(v.children()[0]);
			}
			return o;
		}
	}
}

